package com.xlfc.remoting.transport.netty.server;

import com.xlfc.common.config.RpcConstants;
import com.xlfc.common.config.RpcMessage;
import com.xlfc.common.config.RpcRequest;
import com.xlfc.common.config.RpcResponse;
import com.xlfc.common.enums.RpcResponseCodeEnum;
import com.xlfc.common.exception.RpcException;
import com.xlfc.common.factory.SingletonFactory;
import com.xlfc.registry.ServiceProvider;
import com.xlfc.registry.zk.ZkServiceProviderImpl;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    private static Logger log = LoggerFactory.getLogger(NettyServerHandler.class);

    private final ServiceProvider serviceProvider= SingletonFactory.getInstance(ZkServiceProviderImpl.class);

    //维护chnnelId和具体地址的map,当发生变化时对其进行删除
    //private static ConcurrentHashMap<String, String> channelUrlMap;
    //活跃次数
    private int inActiveCount = 0;
    //开始计数时间
    private long start;
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            if (msg instanceof RpcMessage) {

                RpcResponse<Object> rpcResponse;
                byte messageType = ((RpcMessage) msg).getMessageType();
                RpcMessage rpcMessage=new RpcMessage();

                if (messageType == RpcConstants.HEARTBEAT_REQUEST_TYPE) {
                    rpcMessage.setMessageType(RpcConstants.HEARTBEAT_RESPONSE_TYPE);
                    rpcMessage.setData(RpcConstants.PONG);
                } else {

                    RpcRequest rpcRequest = (RpcRequest) ((RpcMessage) msg).getData();

                    log.info("服务端接收一条新消息：请求id为"+rpcRequest.getRequestId()+"，接口为"+rpcRequest.getInterfaceName()+"，方法为"+rpcRequest.getMethodName());

                    Object result = this.handlerRequest(rpcRequest);
                    rpcMessage.setMessageType(RpcConstants.RESPONSE_TYPE);

                    if (ctx.channel().isActive() && ctx.channel().isWritable()) {
                        rpcResponse= RpcResponse.success(result, rpcRequest.getRequestId());
                    } else {
                        rpcResponse = RpcResponse.fail(RpcResponseCodeEnum.FAIL);
                    }
                    rpcMessage.setData(rpcResponse);
                }
                ctx.writeAndFlush(rpcMessage).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            }
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

    public Object handlerRequest(RpcRequest invocation) {
        Object service = serviceProvider.getService(invocation.getRpcServiceName());
        Object result;
        try {
            Method method = service.getClass().getMethod(invocation.getMethodName(), invocation.getParamTypes());
            result = method.invoke(service, invocation.getParameters());
        } catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
            throw new RpcException(e.getMessage(), e);
        }
        return result;
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent state = (IdleStateEvent)evt;
            if (state.state().equals(IdleState.READER_IDLE)) {
                log.info("读空闲");
            } else if (state.state().equals(IdleState.WRITER_IDLE)) {
                log.info("写空闲");
            }
            //在一定时间内读写空闲才会关闭链接
            else if (state.state().equals(IdleState.ALL_IDLE)) {
                if (++inActiveCount == 1) {
                    start = System.currentTimeMillis();
                }
                int minute = (int)((System.currentTimeMillis() - start) / (60 * 1000)) + 1;
                log.info("第{}次读写都空闲，计时分钟数{}", inActiveCount, minute);
                if (inActiveCount > 2 && minute < 5) {
                    log.info("服务端关闭");
                    ctx.channel().close();
                } else {
                    if (minute >= 5) {
                        log.info("新周期开始");
                        start = 0;
                        inActiveCount = 0;
                    }
                }
            }
        }
    }

}
